package com.yejq.web.listener;

import java.io.File;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import javassist.bytecode.CodeAttribute;
import javassist.bytecode.LocalVariableAttribute;
import javassist.bytecode.MethodInfo;

import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.yejq.web.model.Request;
import com.yejq.web.model.RequestHolder;
import com.yejq.web.model.RequestMapping;
import com.yejq.web.util.PropertiesUtil;

public class StartupListener implements ServletContextListener {

	private Logger logger = LoggerFactory.getLogger(getClass());

	public void contextDestroyed(ServletContextEvent event) {
		logger.info("Destroying ……");
	}

	/**
	 * <p>
	 * url规则，/classname/methodname+小写后缀, method：放在方法最后：
	 * </P>
	 * <p>
	 * 例子：
	 * </P>
	 * <p>
	 * 1. 类名LiZhiYanController，方法名：kiss，则url=/lizhiyan/kiss, method="都可以"
	 * </p>
	 * <p>
	 * 2. 类名LiZhiYanController，方法名：kissHtml，则url=/lizhiyan/kiss.html,
	 * method="都可以"
	 * </p>
	 * <p>
	 * 3. 类名LiZhiYanController，方法名：kissJsp，则url=/lizhiyan/kiss.jsp, method="都可以"
	 * </p>
	 * <p>
	 * 4. 类名LiZhiYanController，方法名：kissHtmlGet，则url=/lizhiyan/kiss.html,
	 * method="GET"
	 * </p>
	 * <p>
	 * 4. 类名LiZhiYanController，方法名：kissHtmlPost，则url=/lizhiyan/kiss.html,
	 * method="POST"
	 * </p>
	 * <p>
	 * 例外：
	 * </p>
	 * <p>
	 * 1、如果是IndexController则不用加类名；
	 * </p>
	 */
	public void contextInitialized(ServletContextEvent event) {
		logger.info("Initializing ……");

		String annotation = PropertiesUtil.getProperty("annotation");
		Request request = null;
		if ("true".equals(annotation)) {
			request = initFromAnnotation(event);
		} else {
			request = initFromFile(event);
		}

		RequestHolder.setRequest(request);

		Iterator<Entry<String, RequestMapping>> iterator = request.entrySet().iterator();
		while (iterator.hasNext()) {
			Entry<String, RequestMapping> entry = iterator.next();
			logger.info("mapping,url:{},{}", entry.getKey(), entry.getValue());
		}
		logger.info("Initialized.");
	}

	private Request initFromAnnotation(ServletContextEvent event) {
		// 获取所有控制器
		String basePackage = PropertiesUtil.getProperty("annotation.package");
		String packageString = basePackage.replaceAll("\\.",
				"\\\\\\\\");
		String classPath = event.getServletContext().getRealPath(
				"\\WEB-INF\\classes\\" + packageString);
		File srcFile = new File(classPath);
		List<File> files = new ArrayList<File>();
		getFiles(srcFile, files);

		// 将控制器的方法映射为请求列表
		Request request = new Request();
		for (File file : files) {
			String className = basePackage + "." + file.getAbsolutePath().replace(classPath, "")
					.replaceAll("\\\\", ".").replace(".class", "").substring(1);
			try {
				Class<?> clazz = Class.forName(className);
				ClassPool pool = ClassPool.getDefault();
				pool.insertClassPath(new ClassClassPath(clazz));
				CtClass cc = pool.get(clazz.getName());

				String preUrl = "";
				String indexController = PropertiesUtil.getProperty("IndexController");
				String simpleName = clazz.getSimpleName();
				if (!simpleName.equalsIgnoreCase(indexController)) {
					preUrl = "/"
							+ simpleName.substring(0, simpleName.indexOf("Controller"))
									.toLowerCase();
				}

				Method[] methods = clazz.getDeclaredMethods();
				for (Method method : methods) {
					String methodName = method.getName();

					RequestMapping mapping = new RequestMapping();
					mapping.setClassName(className);
					mapping.setMethodName(methodName);

					if (method.isAnnotationPresent(com.yejq.web.annotation.RequestMapping.class)) {
						com.yejq.web.annotation.RequestMapping requestMapping = (com.yejq.web.annotation.RequestMapping) method
								.getAnnotation(com.yejq.web.annotation.RequestMapping.class);
						String value = requestMapping.value();
						String requestMethod = requestMapping.method().toString();

						StringBuilder urlBuilder = new StringBuilder(preUrl + value);
						if (requestMethod != null) {
							urlBuilder.append("#").append(requestMethod);
						} else {
							urlBuilder.append("#ALL");
						}

						mapping.setRequestMethod(requestMethod);
						mapping.setParameterTypes(method.getParameterTypes());

						CtMethod cm = cc.getDeclaredMethod(method.getName());

						// 使用javaassist的反射方法获取方法的参数名
						MethodInfo methodInfo = cm.getMethodInfo();
						CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
						LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute
								.getAttribute(LocalVariableAttribute.tag);
						if (attr == null) {
						}
						String[] paramNames = new String[cm.getParameterTypes().length];
						int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
						for (int i = 0; i < paramNames.length; i++) {
							paramNames[i] = attr.variableName(i + pos);
						}

						mapping.setParamNames(paramNames);

						request.put(urlBuilder.toString(), mapping);
					}

				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return request;
	}

	private Request initFromFile(ServletContextEvent event) {
		// 获取所有控制器
		String classPath = event.getServletContext().getRealPath("\\WEB-INF\\classes\\");
		File srcFile = new File(classPath);
		List<File> files = new ArrayList<File>();
		getFiles(srcFile, files);

		// 将控制器的方法映射为请求列表
		Request request = new Request();
		for (File file : files) {
			String className = file.getAbsolutePath().replace(classPath, "")
					.replaceAll("\\\\", ".").replace(".class", "").substring(1);
			try {
				Class<?> clazz = Class.forName(className);
				ClassPool pool = ClassPool.getDefault();
				pool.insertClassPath(new ClassClassPath(clazz));
				CtClass cc = pool.get(clazz.getName());

				String preUrl = "";
				String indexController = PropertiesUtil.getProperty("IndexController");
				String simpleName = clazz.getSimpleName();
				if (!simpleName.equalsIgnoreCase(indexController)) {
					preUrl = "/"
							+ simpleName.substring(0, simpleName.indexOf("Controller"))
									.toLowerCase();
				}

				Method[] methods = clazz.getDeclaredMethods();
				for (Method method : methods) {
					StringBuilder urlBuilder = new StringBuilder(preUrl);
					String methodName = method.getName();

					RequestMapping mapping = new RequestMapping();
					mapping.setClassName(className);
					mapping.setMethodName(methodName);

					String requestMethod = null;
					String requestMethods = PropertiesUtil.getProperty("requestMethods");
					String[] requestMethodArr = requestMethods.split(",");
					for (String rm : requestMethodArr) {
						if (methodName.endsWith(rm)) {
							requestMethod = rm.toUpperCase();
							methodName = methodName.substring(0, methodName.indexOf(rm));
							break;
						}
					}

					String suffix = null;
					String suffixs = PropertiesUtil.getProperty("file.suffixs");
					String[] suffixArr = suffixs.split(",");
					for (String sf : suffixArr) {
						if (methodName.endsWith(sf)) {
							suffix = sf.toLowerCase();
							methodName = methodName.substring(0, methodName.indexOf(sf));
							break;
						}
					}

					urlBuilder.append("/").append(methodName.toLowerCase());
					if (suffix != null) {
						urlBuilder.append(".").append(suffix);
					}
					mapping.setUrl(urlBuilder.toString());

					if (requestMethod != null) {
						urlBuilder.append("#").append(requestMethod);
					} else {
						urlBuilder.append("#ALL");
					}

					mapping.setRequestMethod(requestMethod);
					mapping.setParameterTypes(method.getParameterTypes());

					CtMethod cm = cc.getDeclaredMethod(method.getName());

					// 使用javaassist的反射方法获取方法的参数名
					MethodInfo methodInfo = cm.getMethodInfo();
					CodeAttribute codeAttribute = methodInfo.getCodeAttribute();
					LocalVariableAttribute attr = (LocalVariableAttribute) codeAttribute
							.getAttribute(LocalVariableAttribute.tag);
					if (attr == null) {
					}
					String[] paramNames = new String[cm.getParameterTypes().length];
					int pos = Modifier.isStatic(cm.getModifiers()) ? 0 : 1;
					for (int i = 0; i < paramNames.length; i++) {
						paramNames[i] = attr.variableName(i + pos);
					}

					mapping.setParamNames(paramNames);

					request.put(urlBuilder.toString(), mapping);
				}
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return request;
	}

	/**
	 * 获取所有控制器文件
	 * 
	 * @param file
	 * @param files
	 */
	public static void getFiles(File file, List<File> files) {
		File[] listFiles = file.listFiles();
		for (File f : listFiles) {
			if (f.isDirectory()) {
				getFiles(f, files);
			} else if (f.getName().endsWith("Controller.class")) {
				files.add(f);
			}
		}
	}

}
